//
//  JSONRequest.h
//  nonghubao
//
//  Created by 王 文彬 on 13-1-26.
//  Copyright (c) 2013年 mypocketbaby. All rights reserved.
//

/*
 说明
 
 本类是一个用于建立针对项目专的，从网络获取JSON或发送JSON数据的工具类。并可以在异步的时候显示一个可配置的HUD，用于提示用户正在进行的工作。

 》synchronous是从URL异步请求数据。状态通过协议方法通知调用者。
 》request是从URL同步请求数据
 》setHudOn是使用window作为父视图，用于有键盘遮罩的情况，以避免被遮住。setHud:withView:是用于没有键盘遮罩情况的。
 
*/

#import "Helper.h"
#import "JSONRequest.h"
#import "Config.h"
#import "AFHTTPClient.h"
#import "AFJSONRequestOperation.h"
#import "ASIHTTPRequest.h"
#import "ASIFormDataRequest.h"
#import "Option.h"
#import "DBKit.h"

@implementation JSONRequest

@synthesize delegate            = _delegate;
@synthesize hudTitle            = _hudTitle;
@synthesize hudDetailText       = _hudDetailText;
@synthesize hudCustomView       = _hudCustomView;
@synthesize hudOn               = _hudOn;
@synthesize successSelector     = _sucessSelector;
@synthesize failSelector        = _failSelector;
@synthesize requestMode         = _requestMode;
@synthesize requestParameters   = _requestParameters;


- (id)init
{
    self = [super init];
    if (self) {
        
    }
    return self;
}

// 通过一个连接地址来构建实例
//
- (id)initWithURLString:(NSString *)URLString
{
    self = [super init];
    if (self) {
        
        _url = [NSURL URLWithString:URLString];
        _URLString = URLString;
    }
    
    return self;
}


//2小时退出机制  -- 暂停使用
//- (void)expiredChecking
//{
//    NSDate* now = [NSDate date];
//    long timestamp = [now timeIntervalSince1970];
//
//    Option* option = [[DBKit sharedDBKit] optionWithName:@"lastQueryTime"];
//    //如果不存在，就保存一个当前时间
//    if (!option || option.value.length == 0) {
//        option = [[Option alloc] init];
//        option.name = @"lastQueryTime";
//        option.value = [NSString stringWithFormat:@"%ld", timestamp];
//
//        [[DBKit sharedDBKit] save:option];
//    }
//    //如果存在，就作对比
//    else {
//        
//        long lastQueryTime = [option.value longLongValue];
//        long diffTime = timestamp - lastQueryTime;
//        
////        NSLog(@"%ld %ld %ld", timestamp, lastQueryTime, diffTime);
//        //超过2小时就退出登陆
//        if (diffTime >= 60 * 60 * 2) {
//            
//            [Helper logout];
//        }
//        //否则更新一下时间
//        else {
//            
//            option.value = [NSString stringWithFormat:@"%ld", timestamp];
//            [[DBKit sharedDBKit] save:option];
//        }
//    }
//}


- (void)setURLString:(NSString *)URLString
{
    _url = [NSURL URLWithString:URLString];
    _URLString = URLString;
}

// 设置成功返回时调用的SEL
//
- (void)setSuccessSelector:(SEL)selector delegate:(__weak id)delegate
{
    _sucessSelector = selector;
    _delegate = delegate;
}

// 设置失败返回时调用的SEL
//
- (void)setFailSelector:(SEL)selector delegate:(__weak id)delegate
{
    _failSelector = selector;
    _delegate = delegate;
}

// 获取请求参数。通过self.requestParameters方式，可以触发方法内的逻辑，获取到整理好的字典对象
//
- (NSDictionary *)requestParameters
{
    if (_requestParameters == nil) {
        _requestParameters = [[NSMutableDictionary alloc] init];
    }
    
    if (!_requestParameters[@"token"]) {
        [_requestParameters setValue:_ACCOUNT_TOKEN forKey:@"token"];
    }
    else if (_requestParameters[@"token"] != nil && [_requestParameters[@"token"] isEqualToString:@""]) {
        [_requestParameters removeObjectForKey:@"token"];
    }
    
    if (!_requestParameters[@"appKey"]) {
        [_requestParameters setValue:_CONFIG_APP_KEY forKey:@"appKey"];
    }
    
    return _requestParameters;
}

// 设定参数表
//
- (void)setValue:(id)value forParameter:(NSString *)name
{
    [self.requestParameters setValue:value forKey:name];
}

// 使用内部参数表，同步请求，并返回结果
//
- (NSDictionary *)synchronous
{
    return [self synchronous:_requestMode withParameters:self.requestParameters];
}

// 使用内部参数表，异步请求
- (void)asynchronous
{
    [self asynchronous:_requestMode withParameters:self.requestParameters];
}


// 同步请求，阻塞方式请求数据，并立即返回错误或结果。
// 同步请求的RequestModeMultiPart模式，支持UIImage和NSData两种文件格式
//
- (NSDictionary *)synchronous:(RequestMode)mode withParameters:(NSDictionary *)params
{
    NSLog(@"req: %@ %@", _url ,params);
    
    NSDictionary* result;  //返回结果
    
    //使用ASIFormDataRequest
    ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:_url];

    //根据传入的的Mode，设定post的格式
    if (mode == RequestModeMultiPart) {
        request.postFormat = ASIMultipartFormDataPostFormat;
    }
    else if (mode == RequestModeNormal) {
        request.postFormat = ASIURLEncodedPostFormat;
    }

    //遍历一次传入的参数，并装入请求内部
    //如果发现参数中有UIImage和NSData类型，先判断Mode方式，是multipart就调用ASI的方法装入文件。其他类型，直接装入
    for (id key in params) {
        NSObject *dataParam = [params valueForKey:key];

        //如果是UIImage
        if ([dataParam isKindOfClass:[UIImage class]])
        {
            //在multipart模式下就装入
            if (mode == RequestModeMultiPart) {
                NSData* imageData = UIImagePNGRepresentation((UIImage *)dataParam);
                [request addData:imageData withFileName:[NSString stringWithFormat:@"avatr_%@.png", key] andContentType:@"image/png" forKey:key];
            }
        }
        //如果是NSData
        else if ([dataParam isKindOfClass:[NSData class]])
        {
            //在multipart模式下就装入
            if (mode == RequestModeMultiPart) {
                [request addData:dataParam withFileName:[NSString stringWithFormat:@"avatr_%@.png", key] andContentType:@"image/png" forKey:key];
            }
        }
        //如果是其他类型，无论如何都要装入
        else {
            [request setPostValue:[params objectForKey:key] forKey:key];
        }
    }
    

    ////////
    // --------------->>>>>>> 此处有个错误，待查。打开注释后，点击一次登录，登录失败情况下，再点击登录，bad_access
    ////////
    //开始同步请求。开始前，检测如果设定了_hudOn=true，就要显示菊花
    //显示HUD前，检测是否传入了uiview引用
    //if (_hudOn == true && _hudView != nil) {

    //    [_hud showAnimated:true whileExecutingBlock:^{
            //同步请求
    //        [request startSynchronous];
                
    //    } completionBlock:^{
            //whileExecuteBlock中语句完成时，消除hud
    //        [self hudRemove];
    //    }];
        
    //    [_hud show:true];
    //    [request startSynchronous];
    //    [self hudRemove];
    //}
    //如果没有hud就直接执行同步
    //else {

        [request startSynchronous];
    //}

    
    //从请求中获取错误，如果没有错误，就将结果转换成JSON格式
    NSError *error = [request error];
    if(error == nil)
    {
        NSData* response = [request responseData];
//        NSLog(@"%@-------", response);
        //JSON转换，并用返回结果变量接受转换结果
        result = [NSJSONSerialization JSONObjectWithData:response options:NSJSONReadingMutableLeaves error:&error];
//        NSLog(@"%@=======", result);
        //DEBUG用 - 测试JSON转换是否有错
        if (error != nil) {
            NSLog(@"\n\n!! JSONRequest.request: ERROR: !!\n\n%@", error);
            [Helper showUTF8String:[[NSString alloc] initWithData:response encoding:NSUTF8StringEncoding]];
            NSLog(@"\n\n!! -- ERROR END -- !!\n\n");
        }

    }
    
    NSInteger status = [[result objectForKey:@"status"] integerValue];
    
    if (status == 4) {
        [Helper logout];
    }
    //DEBUG用
//    NSLog(@"\n\n-- DEBUG [JSONRequest.request:%@] --\n\n%@", _url, result);
    
    return result;
}

// 异步模式，使用异步方式请求，请求后调用协议方法返回数据
// 异步请求的RequestModeMultiPart模式，支持UIImage和NSData两种文件格式
//
- (void)asynchronous:(RequestMode)mode withParameters:(NSDictionary *)params
{
    NSLog(@"syn:%@ %@", _url, params);
    
    //检测如果设定了_hudOn=true，就要显示菊花
    //显示HUD前，检测hud
    if (_hudOn == true && _hud != nil) {
        
        [_hud show:true];
    }
    
    //使用AFHTTPRequest
    [AFJSONRequestOperation addAcceptableContentTypes:[NSSet setWithObject:@"text/html"]];  //必须设定
    
    //修复最后斜杠问题
//    NSArray* urlTemp = [[_URLString componentsSeparatedByString:@"//"][1] componentsSeparatedByString:@"/"];
//    NSString* host = [NSURL URLWithString:[NSString stringWithFormat:@"http://%@", urlTemp[0]]];
//
//    NSMutableArray* pathArray = [[NSMutableArray alloc] init];
//    for (int i = 1; i < urlTemp.count; i++) {
//        
//        [pathArray addObject:urlTemp[i]];
//    }
//    NSString* path = [pathArray componentsJoinedByString:@"/"];
    
    AFHTTPClient* httpClient = [[AFHTTPClient alloc] initWithBaseURL:_url];

    //根据不同的mode，实例不同的请求模式
    NSMutableURLRequest *request;
    if (mode == RequestModeNormal) {
        request = [httpClient requestWithMethod:@"POST" path:nil parameters:params];
        request.timeoutInterval = 30.f;
    }
    else if (mode == RequestModeMultiPart) {
        request = [httpClient multipartFormRequestWithMethod:@"POST" path:nil parameters:params constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
            
            //遍历一次传入的参数，并构造请求
            //如果发现参数中有UIImage和NSData类型，先判断Mode方式，是multipart就调用块变量进行构造
            for (id key in params) {
                NSObject *dataParam = [params valueForKey:key];
                
                //如果是UIImage
                if ([dataParam isKindOfClass:[UIImage class]])
                {
                    //在multipart模式下就装入
                    if (mode == RequestModeMultiPart) {
                        
                        NSData* imageData = UIImagePNGRepresentation((UIImage *)dataParam);
                        [formData appendPartWithFileData:imageData name:key fileName:[NSString stringWithFormat:@"avatr_%@.png", key] mimeType:@"image/png"];
                    }
                }
                //如果是NSData
                else if ([dataParam isKindOfClass:[NSData class]])
                {
                    //在multipart模式下就构造
                    if (mode == RequestModeMultiPart) {
                        [formData appendPartWithFileData:(NSData *)dataParam name:key fileName:[NSString stringWithFormat:@"avatr_%@.png", key] mimeType:@"image/png"];
                    }
                }
            }
            
        }];
        request.timeoutInterval = 30.f;
    }
    
    //准备异步
    AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest* request, NSHTTPURLResponse* response, id JSON) {
        
        //成功时，本Block会被调用
        
        //请求成功和失败，无论如何都要调用hudRemove
        [self hudRemove];
        
//        NSLog(@"\n\n-- DEBUG [JSONRequest.synchronous : %@] -- \n\n%@", _url, JSON);
        //如果token失效那么注销推送通知
        NSInteger status = [[(NSDictionary *)JSON objectForKey:@"status"] integerValue];
        
        if (status == 4) {
            
            [Helper logout];
            
            //如果已经登出过了，就不再弹窗了
            Option* option = [[DBKit sharedDBKit] optionWithName:@"hadLogout"];
            BOOL hadLogout = [option.value boolValue];
            if (hadLogout) {
                [Helper showAlertWithTitle:nil andMessage:((NSDictionary *)JSON)[@"message"]];
            }
            return;
        }
        
        //调用私有方法，发出协议
        [self performSuccessSelector:JSON];
                
    } failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) {
        
        //请求失败时，本Block会被调用
        NSLog(@"\n\n-- DEBUG [JSONRequest.asynchronous] -- \n\nERROR=%@ %@", error, JSON);

        //调用私有方法，发出协议
        [self performFailSelector:nil];

        //请求成功和失败，无论如何都要调用hudRemove
        [self hudRemove];
    }];
    
    //开始异步
    [operation start];  //这里也可以使用队列方式
}


// 成功时调用协议方法，如果设置了指定的，就用指定的selector
//
- (void)performSuccessSelector:(id)object
{
    //如果设定了代理
    if (_delegate) {
        
        //如果也设定了成功时的selector，就调用协议下指定的这个selector
        if (_sucessSelector) {
            [_delegate performSelector:_sucessSelector withObject:object];
        }
        
        //否则就调用协议默认指定的selector
        else {
            [_delegate requestDidLoadJSON:object];
        }
        
    }
}


// 失败时时调用协议方法，如果设置了指定的，就用指定的selector
//
- (void)performFailSelector:(id)object
{
    //如果设定了代理
    if (_delegate) {
        
        //如果也设定了失败时的selector，就调用协议下指定的这个selector
        if (_failSelector) {
            [_delegate performSelector:_failSelector];
        }
        //否则就调用协议默认指定的selector
        else {
            [_delegate requestFailWithNetworkUnavailabe];
        }
        
    }
    
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////

#pragma mark - HUD相关

// HUD设置方法。通过view来启动HUD
//
- (void)setHUD:(HUDStyleMode)mode withView:(UIView *)view
{
    _hudOn = true;
    
    if (view != nil) {
        
        _hudView = view;

        _hud = [MBProgressHUD showHUDAddedTo:view animated:true];
        _hud.mode = mode;
        _hud.labelText = _hudTitle;
        _hud.detailsLabelText = _hudDetailText;
        _hud.customView = _hudCustomView;
    }
}

// HUD设置方法。直接启动，至于window上面，而不是view上面
//
- (void)setHudOn:(BOOL)hudOn
{
    _hudOn = hudOn;
    
    if (_hudOn == true) {
        //有键盘遮罩的问题，一定要建立在最后一个Window上面，不然后被键盘挡住
        UIWindow* window = [[UIApplication sharedApplication].windows objectAtIndex:([UIApplication sharedApplication].windows.count - 1)];
        _hud = [[MBProgressHUD alloc] initWithWindow:window];
        [window addSubview:_hud];
        
        _hud.mode = _hudMode;
        _hud.labelText = _hudTitle;
        _hud.detailsLabelText = _hudDetailText;
        _hud.customView = _hudCustomView;

    }
}

- (void)setHudCustomView:(UIView *)hudCustomView
{
    _hudCustomView = hudCustomView;
    _hud.customView = hudCustomView;
}

- (void)setHudDetailText:(NSString *)hudDetailText
{
    _hudDetailText = hudDetailText;
    _hud.detailsLabelText = hudDetailText;
}

- (void)setHudTitle:(NSString *)hudTitle
{
    _hudTitle = hudTitle;
    _hud.labelText = hudTitle;
}


/////////////////////////////////////////////////////////////////////////////////////////////////////////

#pragma mark - 私有方法

// 私有方法，用于移除hud
//
- (void)hudRemove
{
    if (_hudOn == true && _hud != nil) {
        _hud.hidden = true;
        [_hud removeFromSuperview];
    }
}



@end
